﻿using Autoupdate.Common;
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace Autoupdate
{
    public class AutoupdateClient
    {
        readonly string logSecion = "程序更新";
        readonly string dbFileName = Application.StartupPath + "\\Update\\DBSetting.xml";
        readonly string configFile = Application.StartupPath + "\\Update\\File.cfg";

        public void Run()
        {
            Log.FileName = Application.StartupPath + "\\Update\\Log.txt";

            string runFile = string.Empty;
            string preUpdateFile = string.Empty;
            string postUpdateFile = string.Empty;
            string shortcutFile = string.Empty;
            List<string> oldShortcutFiles = new List<string>();

            // 读取配置信息：模块名称，版本锁定状态
            Configuration config = new Configuration(null);
            config.Load(configFile);
            string moduleName = config.ReadString(SysUpdateVersion.Module);
            string locked = config.ReadString(SysUpdateVersion.Locked);

            MarqueeProgressForm form = new MarqueeProgressForm();
            form.Text = "正在连接服务器，请稍候...";
            form.DoBackgroundWork += delegate (object sender, EventArgs e)
            {
                // 连接服务器
                ConnectServerForm connectForm = new ConnectServerForm(dbFileName);
                if (!connectForm.TryConnectEx())
                {
                    config.Load(configFile);
                    runFile = config.ReadString(FileUsage.Run);
                    if (!string.IsNullOrEmpty(runFile))
                        return;
                    if (connectForm.ShowDialog() != DialogResult.OK)
                        return;
                }

                UpdateFormText(form, "正在校验系统，请稍候...");
                if (string.IsNullOrEmpty(moduleName))
                {
                    MessageBox.Show("未指定系统校验模块名称，系统校验失败！",
                        "错误",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);
                    return;
                }
                using (DataTable serverFileTable = new DataTable())
                {
                    if (string.IsNullOrEmpty(locked) || locked != "1")
                        locked = "0";
                    DBHelper.FillTable("EXEC Sys_Update_GetFiles " + moduleName + "," + locked, serverFileTable);
                    if (serverFileTable.Rows.Count > 0)
                    {
                        serverFileTable.Columns.Add(SysUpdateFile.Status);

                        List<string> localFiles = Autoupdate.Common.Common.GetLocalFiles(Application.StartupPath);
                        List<int> serverExtraFiles = new List<int>();
                        List<int> serverUpdateFiles = new List<int>();
                        Common.Common.CompareFiles(Application.StartupPath, serverFileTable, localFiles, serverExtraFiles, serverUpdateFiles, null, null);
                        // TODO: serverUpdateFiles的文件全部是Ignore而本地存在时，不需要执行更新
                        if (serverExtraFiles.Count != 0 || NeedUpdate(serverFileTable, serverUpdateFiles))// serverUpdateFiles.Count != 0)
                        {
                            using (DataTable table = new DataTable())
                            {
                                // 查找文件中最大的版本Id
                                int versionId = int.Parse(serverFileTable.Rows[0][SysUpdateVersion.Id].ToString());
                                int tmpId = -1;
                                foreach (DataRow dr in serverFileTable.Rows)
                                {
                                    tmpId = int.Parse(dr[SysUpdateVersion.Id].ToString());
                                    if (tmpId > versionId)
                                        versionId = tmpId;
                                }
                                DBHelper.FillTable("SELECT * FROM " + SysUpdateVersion.TableName
                                    + " WHERE " + SysUpdateVersion.Id + "=" + versionId.ToString(), table);
                                MessageBox.Show("发现更新版本：" + table.Rows[0][SysUpdateVersion.Name].ToString()
                                    + "\n\n更新日志："
                                    + "\n\n" + table.Rows[0][SysUpdateVersion.Description].ToString(),
                                    "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                            }

                            using (DataTable fileTable = serverFileTable.Copy())
                            {
                                fileTable.Rows.Clear();
                                foreach (int i in serverExtraFiles)
                                    fileTable.ImportRow(serverFileTable.Rows[i]);
                                foreach (int i in serverUpdateFiles)
                                    fileTable.ImportRow(serverFileTable.Rows[i]);

                                string time = DateTime.Now.ToString();
                                Log.WriteInfo(logSecion, "\n\n更新日志 " + time + " 开始----------------------------------------------------");
                                //
                                // 确保本地所有的EXE文件都不再执行才进行更新
                                //
                                List<string> localExeFiles = new List<string>();
                                List<string> localRunningFiles = new List<string>();
                                foreach (string file in localFiles)
                                {
                                    if (file.ToUpper().EndsWith(".EXE"))
                                        localExeFiles.Add(Path.GetFileNameWithoutExtension(file));
                                }
                                while (true)
                                {
                                    localRunningFiles.Clear();
                                    Process[] processesByName = Process.GetProcesses();
                                    foreach (Process process in processesByName)
                                    {
                                        if (!localExeFiles.Contains(process.ProcessName))
                                            continue;
                                        localRunningFiles.Add(process.ProcessName);
                                    }
                                    if (localRunningFiles.Count == 0)
                                        break;
                                    StringBuilder builder = new StringBuilder();
                                    builder.Append("发现以下程序正在运行：");
                                    foreach (string file in localRunningFiles)
                                        builder.Append("\n" + file);
                                    builder.Append("\n以上全部程序需要全部关闭后才能正常更新，");
                                    builder.Append("\n点击确定后以上程序会强制关闭，确定继续吗？");
                                    DialogResult result = MessageBox.Show(builder.ToString(),
                                        "警告", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                                    if (result != DialogResult.Yes)
                                        return;
                                    foreach (Process process in processesByName)
                                    {
                                        if (!localRunningFiles.Contains(process.ProcessName))
                                            continue;
                                        process.Kill();
                                        process.WaitForExit();
                                        process.Close();
                                    }

                                    Log.WriteInfo(logSecion, "\n强制关闭以下程序" + builder.ToString());
                                }

                                //
                                // 
                                //
                                int runFileVersion = 0, preUpdateFileVersion = 0, postUpdateFileVersion = 0, shortcutFileVersion = 0;
                                for (int i = 0; i < serverFileTable.Rows.Count; ++i)
                                {
                                    int version = int.Parse(serverFileTable.Rows[i][SysUpdateVersion.Id].ToString());
                                    if (serverFileTable.Rows[i][SysUpdateFile.Usage].ToString() == FileUsage.Run)
                                    {
                                        if (version > runFileVersion)
                                        {
                                            runFileVersion = version;
                                            runFile = serverFileTable.Rows[i][SysUpdateFile.Path].ToString();
                                        }
                                    }
                                    else if (serverFileTable.Rows[i][SysUpdateFile.Usage].ToString() == FileUsage.PreUpdate)
                                    {
                                        if (version > preUpdateFileVersion)
                                        {
                                            preUpdateFileVersion = version;
                                            preUpdateFile = serverFileTable.Rows[i][SysUpdateFile.Path].ToString();
                                        }

                                        if (!serverExtraFiles.Contains(i) && !serverUpdateFiles.Contains(i))
                                            continue;
                                        string path = Path.Combine(Application.StartupPath, Path.GetDirectoryName(serverFileTable.Rows[i][SysUpdateFile.Path].ToString()));
                                        if (!Directory.Exists(path))
                                            Directory.CreateDirectory(path);
                                        Util.StreamToFile(Util.BytesToStream((byte[])serverFileTable.Rows[i][SysUpdateFile.Data]), serverFileTable.Rows[i][SysUpdateFile.Path].ToString());

                                        Log.WriteInfo(logSecion, "\n下载更新前执行程序:" + preUpdateFile);
                                    }
                                    else if (serverFileTable.Rows[i][SysUpdateFile.Usage].ToString() == FileUsage.PostUpdate)
                                    {
                                        if (version > postUpdateFileVersion)
                                        {
                                            postUpdateFileVersion = version;
                                            postUpdateFile = serverFileTable.Rows[i][SysUpdateFile.Path].ToString();
                                        }
                                    }
                                    else if (serverFileTable.Rows[i][SysUpdateFile.Usage].ToString() == FileUsage.Icon)
                                    {
                                        if (version > shortcutFileVersion)
                                        {
                                            if (!string.IsNullOrEmpty(shortcutFile))
                                                oldShortcutFiles.Add(shortcutFile);
                                            shortcutFileVersion = version;
                                            shortcutFile = serverFileTable.Rows[i][SysUpdateFile.Path].ToString();
                                        }
                                    }
                                }

                                // 执行更新前要执行的文件
                                if (!string.IsNullOrEmpty(preUpdateFile))
                                {
                                    Log.WriteInfo(logSecion, "\n执行更新前执行程序 " + preUpdateFile + " 开始 -----------");
                                    Process proc = Process.Start(Path.Combine(Application.StartupPath, preUpdateFile));
                                    if (proc != null)
                                        proc.WaitForExit(); // 无限等待程序退出
                                    Log.WriteInfo(logSecion, "\n执行更新前执行程序 " + preUpdateFile + " 结束 -----------");
                                }

                                DownloadForm downloadForm = new DownloadForm(fileTable);
                                downloadForm.DoBackgroundWork += delegate (object sender1, EventArgs e1)
                                {
                                    // 更新文件
                                    Log.WriteInfo(logSecion, "\n更新本地文件 开始");
                                    for (int i = 0; i < fileTable.Rows.Count; ++i)
                                    {
                                        string file = Path.Combine(Application.StartupPath, fileTable.Rows[i][SysUpdateFile.Path].ToString());
                                        string path = Path.GetDirectoryName(file);
                                        //UpdateFormText(form, "正在更新系统(" + (i + 1).ToString() + "/" + fileTable.Rows.Count + "): " + fileTable.Rows[i][SysUpdateFile.Name].ToString() + "...");

                                        // 执行更新文件跳过
                                        if (fileTable.Rows[i][SysUpdateFile.Usage].ToString() == FileUsage.PreUpdate)
                                        {
                                            DownloadReportProgress(downloadForm, i, "完成");
                                            continue;
                                        }
                                            
                                        // 忽略更新的文件，如果本地存在，则跳过，如果不存在，则添加
                                        if (fileTable.Rows[i][SysUpdateFile.Usage].ToString() == FileUsage.Ignore)
                                        {
                                            if (File.Exists(file))
                                            {
                                                DownloadReportProgress(downloadForm, i, "完成");
                                                continue;
                                            }      
                                        }
                                        Log.WriteInfo(logSecion, "\n更新本地文件: " + fileTable.Rows[i][SysUpdateFile.Path].ToString());
                                        if (!Directory.Exists(path))
                                            Directory.CreateDirectory(path);
                                        Util.StreamToFile(Util.BytesToStream((byte[])fileTable.Rows[i][SysUpdateFile.Data]), file);

                                        DownloadReportProgress(downloadForm, i, "完成");
                                    }
                                    Log.WriteInfo(logSecion, "\n更新本地文件 结束");
                                };
                                if (downloadForm.ShowDialog() != DialogResult.OK)
                                    return;

                                // 执行更新后要执行的文件
                                if (!string.IsNullOrEmpty(postUpdateFile))
                                {
                                    Log.WriteInfo(logSecion, "\n执行更新后执行程序 " + postUpdateFile + " 开始");
                                    Process proc = Process.Start(Path.Combine(Application.StartupPath, postUpdateFile));
                                    if (proc != null)
                                        proc.WaitForExit(); // 无限等待程序退出
                                    Log.WriteInfo(logSecion, "\n执行更新后执行程序 " + postUpdateFile + " 结束");
                                }

                                Log.WriteInfo(logSecion, "\n更新日志 " + time + " 结束----------------------------------------------------\n");
                            }
                        }
                        else
                        {
                            int runFileVersion = 0, shortcutFileVersion = 0;
                            for (int i = 0; i < serverFileTable.Rows.Count; ++i)
                            {
                                int version = int.Parse(serverFileTable.Rows[i][SysUpdateVersion.Id].ToString());
                                if (serverFileTable.Rows[i][SysUpdateFile.Usage].ToString() == FileUsage.Run)
                                {
                                    if (version > runFileVersion)
                                    {
                                        runFileVersion = version;
                                        runFile = serverFileTable.Rows[i][SysUpdateFile.Path].ToString();
                                    }
                                }
                                else if (serverFileTable.Rows[i][SysUpdateFile.Usage].ToString() == FileUsage.Icon)
                                {
                                    if (version > shortcutFileVersion)
                                    {
                                        if (!string.IsNullOrEmpty(shortcutFile))
                                            oldShortcutFiles.Add(shortcutFile);
                                        shortcutFileVersion = version;
                                        shortcutFile = serverFileTable.Rows[i][SysUpdateFile.Path].ToString();
                                    }
                                }
                            }
                        }
                    }
                }

                config.WriteString(FileUsage.Run, runFile, true);
                config.Save(configFile);
                config.Load(configFile);
            };
            form.ShowDialog();

            if (string.IsNullOrEmpty(moduleName))
            {
                MessageBox.Show("未指定系统模块名称，启动失败！",
                    "错误",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
                return;
            }

            // 不能从服务器获取启动文件，从本地读取
            if (string.IsNullOrEmpty(runFile))
            {
                config.Load(configFile);
                runFile = config.ReadString(FileUsage.Run);
            }

            if (string.IsNullOrEmpty(runFile))
            {
                MessageBox.Show("找不到启动文件，启动失败！",
                    "错误",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
                return;
            }

            oldShortcutFiles.Add(runFile);

            string desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            const string manualCreateShortcutKey = "ManualCreateShortcut";
            bool manualCreateShortcut = config.ReadBoolean(manualCreateShortcutKey);
            try
            {
                if (!manualCreateShortcut)
                {
                    Log.WriteInfo(logSecion, "\n创建快捷方式到： " + desktop + "\n执行路径：" + Application.ExecutablePath);
                    // 使用图标文件创建桌面快捷菜单
                    if (string.IsNullOrEmpty(shortcutFile))
                    {
                        shortcutFile = runFile;
                        Util.CreateShortcut(desktop, Path.GetFileNameWithoutExtension(runFile), Application.ExecutablePath);
                    }  
                    else
                        Util.CreateShortcut(desktop, Path.GetFileNameWithoutExtension(shortcutFile), Application.ExecutablePath, string.Empty, Path.Combine(Application.StartupPath, shortcutFile));

                    config.WriteBoolean(manualCreateShortcutKey, false, true);
                    config.Save(configFile);
                }
            }
            catch (Exception ex)
            {
                DialogResult result = MessageBox.Show("创建快捷方式失败，需要手动创建吗?",
                    "错误",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Error);
                config.WriteBoolean(manualCreateShortcutKey, result == DialogResult.OK, true);
                config.Save(configFile);
                Log.WriteError(logSecion, ex.Message + ex.StackTrace);
            }

            // 删除旧的快捷方式
            string currentShortcutFile = Path.GetFileNameWithoutExtension(shortcutFile);
            foreach (string file in oldShortcutFiles)
            {
                string tmpFile = Path.GetFileNameWithoutExtension(file);
                if (file == currentShortcutFile)
                    continue;
                string shortcutPath = Path.Combine(desktop, string.Format("{0}.lnk", tmpFile));
                if (File.Exists(shortcutPath))
                    File.Delete(shortcutPath);
            }

            Application.Exit();
            Process.Start(Path.Combine(Application.StartupPath, runFile));
        }

        private bool NeedUpdate(DataTable serverFileTable, List<int> serverUpdateFiles)
        {
            if (serverUpdateFiles.Count == 0)
                return false;
            // 只要有一个不是忽略的文件，则进行更新
            foreach (int i in serverUpdateFiles)
            {
                if (serverFileTable.Rows[i][SysUpdateFile.Usage].ToString() != FileUsage.Ignore)
                    return true;
            }
            // 全部是忽略文件，只要有一个在本地不存在，则进行更新
            foreach (int i in serverUpdateFiles)
            {
                string file = Path.Combine(Application.StartupPath, serverFileTable.Rows[i][SysUpdateFile.Path].ToString());
                if (!File.Exists(file))
                    return true;
            }
            return false;
        }

        delegate void UpdateFormTextCallBack(Form form, string text);
        private void UpdateFormText(Form form, string text)
        {
            if (form.InvokeRequired)
                form.Invoke(new UpdateFormTextCallBack(UpdateFormText), new object[] { form, text });
            else
                form.Text = text;
        }

        delegate void DownloadReportProgressCallBack(DownloadForm form, int percentProgress, object userState);
        private void DownloadReportProgress(DownloadForm form, int percentProgress, object userState)
        {
            if (form.InvokeRequired)
                form.Invoke(new DownloadReportProgressCallBack(DownloadReportProgress), new object[] { form, percentProgress, userState });
            else
                form.ReportProgress(percentProgress, userState);
        }
    }
}
